﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace DouEditor.Extend
{
    /// <summary>
    /// 用于快速拾取场景中指定节点下的所有对象
    /// 给定 json 描述的结构后获取该描述下的所有 GameObject 对象
    /// 
    /// JSON描述示例：
    /// { // 所有的 key 都是正则表达式，会和 GameObject 的名称进行匹配
    ///     "MapScene_1": {
    ///         ".*": {
    ///             "Root": {
    ///                 "Tree": 0, // 注意最终节点不能是 map 的结构，其值会回调给 OnPickup 委托
    ///                 "Other": "this is other"
    ///             }
    ///         }
    ///     }
    /// }
    /// 
    /// 组的用法，开启后可以抓取组：
    /// {
    ///     "MapScene_([0-9]+)": { // 第一个组
    ///         "([a-z]+)": { // 第二个组
    ///             "Root": {
    ///                 "Tree_{1}_{0}": 0 // {0} 表示第一个组，{1} 表示第二个组
    ///             }
    ///         }
    ///     }
    /// }
    /// 
    /// </summary>
    public class HierarchyGameObjectPickup
    {
        private static readonly Dictionary<string, Regex> _regexMap = new Dictionary<string, Regex>();

        private Dictionary<string, JToken> _json;
        private bool _ignoreNonactive = false;
        private bool _activeGroup = false;

        private Action<GameObject, object, string[]> _onPickup;

        public HierarchyGameObjectPickup(string desc, bool ignoreNonactive = false, bool activeGroup = false)
        {
            _json = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(desc);
            _ignoreNonactive = ignoreNonactive;
            _activeGroup = activeGroup;
        }

        /// <summary>
        /// 是否忽略掉未激活对象
        /// </summary>
        public bool ignoreNonactive
        {
            set => _ignoreNonactive = value;
            get => _ignoreNonactive;
        }

        /// <summary>
        /// 是否可以抓取组
        /// </summary>
        public bool activeGroup
        {
            set => _activeGroup = value;
            get => _activeGroup;
        }

        /// <summary>
        /// 获取到最终可用 GameObject 时会回调改方法，同时将对应的配置值和对应的组信息传递出去
        /// </summary>
        public Action<GameObject, object, string[]> onPickup
        {
            set => _onPickup = value;
            get => _onPickup;
        }

        /// <summary>
        /// 从当前场景获取
        /// </summary>
        public List<GameObject> Pickup(bool needResult = false)
        {
            return Pickup(EditorSceneManager.GetActiveScene(), needResult);
        }

        /// <summary>
        /// 从指定场景获取
        /// </summary>
        public List<GameObject> Pickup(Scene scene, bool needResult = false)
        {
            List<GameObject> result = null;
            if (needResult)
            {
                result = new List<GameObject>();
            }
            foreach (var child in scene.GetRootGameObjects())
            {
                TryPickup(_json, child, new Stack<string>(), result);
            }
            return result;
        }

        /// <summary>
        /// 从指定 GameObject 中获取
        /// </summary>
        public List<GameObject> Pickup(GameObject gameObject, bool needResult = false)
        {
            List<GameObject> result = null;
            if (needResult)
            {
                result = new List<GameObject>();
            }
            for (int i = 0; i < gameObject.transform.childCount; i++)
            {
                var child = gameObject.transform.GetChild(i).gameObject;
                TryPickup(_json, child, new Stack<string>(), result);
            }
            return result;
        }

        private void TryPickup(Dictionary<string, JToken> json, GameObject gameObject, Stack<string> groupInfo, List<GameObject> result)
        {
            if (_ignoreNonactive && !gameObject.activeSelf)
            {
                return;
            }
            foreach (var item in json)
            {
                var regStr = item.Key;
                if (_activeGroup)
                {
                    var groupArr = groupInfo.ToArray();
                    Array.Reverse(groupArr);
                    regStr = String.Format(regStr, groupArr);
                }
                Regex nameReg;
                if (!_regexMap.TryGetValue(regStr, out nameReg))
                {
                    nameReg = new Regex(regStr);
                    _regexMap.Add(regStr, nameReg);
                }
                var name = gameObject.name;
                // 匹配失败
                if (!nameReg.IsMatch(name))
                {
                    continue;
                }
                var addNum = 0;
                if (_activeGroup)
                {
                    var match = nameReg.Match(name);
                    if (match.Groups.Count > 1)
                    {
                        addNum = match.Groups.Count - 1;
                        for (int i = 1; i < match.Groups.Count; i++)
                        {
                            groupInfo.Push(match.Groups[i].Value);
                        }
                    }
                }
                // 匹配成功可以继续匹配
                var value = item.Value;
                // 字典继续向下查询
                if (value.Type == JTokenType.Object)
                {
                    // 值
                    var v = new Dictionary<string, JToken>();
                    foreach (var property in (value as JObject).Properties())
                    {
                        v.Add(property.Name, property.Value);
                    }
                    // 继续向下遍历
                    for (int i = 0; i < gameObject.transform.childCount; i++)
                    {
                        var child = gameObject.transform.GetChild(i).gameObject;
                        TryPickup(v, child, groupInfo, result);
                    }
                }
                // 其它类型直接作为叶子节点返回
                else
                {
                    // 值
                    object v = null;
                    switch (value.Type)
                    {
                        case JTokenType.Boolean:
                            v = value.Value<bool>();
                            break;
                        case JTokenType.Integer:
                            v = value.Value<int>();
                            break;
                        case JTokenType.Float:
                            v = value.Value<float>();
                            break;
                        case JTokenType.String:
                            v = value.Value<string>();
                            break;
                        case JTokenType.Array:
                            v = value as JArray;
                            break;
                    }
                    // 正则组
                    var groupArr = groupInfo.ToArray();
                    Array.Reverse(groupArr);
                    _onPickup?.Invoke(gameObject, v, groupArr);
                    result?.Add(gameObject);
                }
                if (_activeGroup)
                {
                    for (int i = 0; i < addNum; i++)
                    {
                        groupInfo.Pop();
                    }
                }
            }
        }
    }
}
